/*-------------------------------------------------------------------------
 *
 * portalmem.c--
 *    backend portal memory context management stuff
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/cvsroot/postgres95/src/backend/utils/mmgr/portalmem.c,v 1.3 1996/11/08 06:00:57 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 * NOTES
 *	Do not confuse "Portal" with "PortalEntry" (or "PortalBuffer").
 *	When a PQexec() routine is run, the resulting tuples
 *	find their way into a "PortalEntry".  The contents of the resulting
 *	"PortalEntry" can then be inspected by other PQxxx functions.
 *
 *	A "Portal" is a structure used to keep track of queries of the
 *	form:
 *		retrieve portal FOO ( blah... ) where blah...
 *
 *	When the backend sees a "retrieve portal" query, it allocates
 *	a "PortalD" structure, plans the query and then stores the query
 *	in the portal without executing it.  Later, when the backend
 *	sees a
 *		fetch 1 into FOO
 *
 *	the system looks up the portal named "FOO" in the portal table,
 *	gets the planned query and then calls the executor with a feature of
 *	'(EXEC_FOR 1).  The executor then runs the query and returns a single
 *	tuple.  The problem is that we have to hold onto the state of the
 *	portal query until we see a "close p".  This means we have to be
 *	careful about memory management.
 *
 *	Having said all that, here is what a PortalD currently looks like:
 *
 * struct PortalD {
 *	char*				name;
 *	classObj(PortalVariableMemory)	variable;
 *	classObj(PortalHeapMemory)	heap;
 *	List				queryDesc;
 *	EState				state;
 *	void				(*cleanup) ARGS((Portal portal));
 * };
 *
 *	I hope this makes things clearer to whoever reads this -cim 2/22/91
 *
 *	Here is an old comment taken from nodes/memnodes.h
 *
 * MemoryContext --
 *	A logical context in which memory allocations occur.
 *
 * The types of memory contexts can be thought of as members of the
 * following inheritance hierarchy with properties summarized below.
 *
 *			Node
 *			|
 *		MemoryContext___
 *		/		\
 *	GlobalMemory	PortalMemoryContext
 *			/		\
 *	PortalVariableMemory	PortalHeapMemory
 *
 *			Flushed at	Flushed at	Checkpoints
 *			Transaction	Portal
 *			Commit		Close
 *
 * GlobalMemory			n		n		n
 * PortalVariableMemory		n		y		n
 * PortalHeapMemory		y		y		y *	
 *
 */
#include <stdio.h>		/* for sprintf() */
#include <string.h>	/* for strlen, strncpy */

#include "postgres.h"

#include "lib/hasht.h"
#include "utils/module.h"
#include "utils/excid.h"	/* for Unimplemented */
#include "utils/mcxt.h"
#include "utils/hsearch.h"

#include "nodes/memnodes.h"
#include "nodes/nodes.h"
#include "nodes/pg_list.h"
#include "nodes/execnodes.h"	/* for EState */

#include "utils/portal.h"

/* ----------------
 *   	ALLOCFREE_ERROR_ABORT
 *   	define this if you want a core dump when you try to
 *   	free memory already freed -cim 2/9/91
 * ----------------
 */
#undef ALLOCFREE_ERROR_ABORT

/* ----------------
 *   	Global state
 * ----------------
 */

static int PortalManagerEnableCount = 0;
#define MAX_PORTALNAME_LEN	64	/* XXX LONGALIGNable value */

typedef struct portalhashent {
    char portalname[MAX_PORTALNAME_LEN];
    Portal portal;
} PortalHashEnt;

#define PortalManagerEnabled	(PortalManagerEnableCount >= 1)

static HTAB		*PortalHashTable = NULL;
#define PortalHashTableLookup(NAME, PORTAL) \
    {   PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
	memset(key, 0, MAX_PORTALNAME_LEN); \
	sprintf(key, "%s", NAME); \
	hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
					     key, HASH_FIND, &found); \
	if (hentry == NULL) \
	    elog(FATAL, "error in PortalHashTable"); \
	if (found) \
	    PORTAL = hentry->portal; \
	else \
	    PORTAL = NULL; \
    }
#define PortalHashTableInsert(PORTAL) \
    {   PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
	memset(key, 0, MAX_PORTALNAME_LEN); \
	sprintf(key, "%s", PORTAL->name); \
	hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
					     key, HASH_ENTER, &found); \
	if (hentry == NULL) \
	    elog(FATAL, "error in PortalHashTable"); \
	if (found) \
	    elog(NOTICE, "trying to insert a portal name that exists."); \
	hentry->portal = PORTAL; \
    }
#define PortalHashTableDelete(PORTAL) \
    {   PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
	memset(key, 0, MAX_PORTALNAME_LEN); \
	sprintf(key, "%s", PORTAL->name); \
	hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
					     key, HASH_REMOVE, &found); \
	if (hentry == NULL) \
	    elog(FATAL, "error in PortalHashTable"); \
	if (!found) \
	    elog(NOTICE, "trying to delete portal name that does not exist."); \
    }

static GlobalMemory	PortalMemory = NULL;
static char		PortalMemoryName[] = "Portal";

static Portal		BlankPortal = NULL;

/* ----------------
 * 	Internal class definitions
 * ----------------
 */
typedef struct HeapMemoryBlockData {
    AllocSetData	setData;
    FixedItemData	itemData;
} HeapMemoryBlockData;

typedef HeapMemoryBlockData	*HeapMemoryBlock;

#define HEAPMEMBLOCK(context) \
    ((HeapMemoryBlock)(context)->block)

/* ----------------------------------------------------------------
 *		  Variable and heap memory methods
 * ----------------------------------------------------------------
 */
/* ----------------
 *	PortalVariableMemoryAlloc
 * ----------------
 */
static Pointer
PortalVariableMemoryAlloc(PortalVariableMemory this,
			  Size size)
{
    return (AllocSetAlloc(&this->setData, size));
}

/* ----------------
 *	PortalVariableMemoryFree
 * ----------------
 */
static void
PortalVariableMemoryFree(PortalVariableMemory this,
			 Pointer	pointer)
{
    AllocSetFree(&this->setData, pointer);
}

/* ----------------
 *	PortalVariableMemoryRealloc
 * ----------------
 */
static Pointer
PortalVariableMemoryRealloc(PortalVariableMemory this,
			    Pointer pointer,
			    Size size)
{
    return (AllocSetRealloc(&this->setData, pointer, size));
}

/* ----------------
 *	PortalVariableMemoryGetName
 * ----------------
 */
static char*
PortalVariableMemoryGetName(PortalVariableMemory this)
{
    return (form("%s-var", PortalVariableMemoryGetPortal(this)->name));
}

/* ----------------
 *	PortalVariableMemoryDump
 * ----------------
 */
static void
PortalVariableMemoryDump(PortalVariableMemory this)
{
    printf("--\n%s:\n", PortalVariableMemoryGetName(this));
    
    AllocSetDump(&this->setData);	/* XXX is this the right interface */
}

/* ----------------
 *	PortalHeapMemoryAlloc
 * ----------------
 */
static Pointer
PortalHeapMemoryAlloc(PortalHeapMemory this,
		      Size size)
{
    HeapMemoryBlock	block = HEAPMEMBLOCK(this);
    
    AssertState(PointerIsValid(block));
    
    return (AllocSetAlloc(&block->setData, size));
}

/* ----------------
 *	PortalHeapMemoryFree
 * ----------------
 */
static void
PortalHeapMemoryFree(PortalHeapMemory this,
		     Pointer pointer)
{
    HeapMemoryBlock	block = HEAPMEMBLOCK(this);
    
    AssertState(PointerIsValid(block));
    
    if (AllocSetContains(&block->setData, pointer))
	AllocSetFree(&block->setData, pointer);
    else {
	elog(NOTICE,
	     "PortalHeapMemoryFree: 0x%x not in alloc set!",
	     pointer);
#ifdef ALLOCFREE_ERROR_ABORT
	Assert(AllocSetContains(&block->setData, pointer));
#endif /* ALLOCFREE_ERROR_ABORT*/
    }
}

/* ----------------
 *	PortalHeapMemoryRealloc
 * ----------------
 */
static Pointer
PortalHeapMemoryRealloc(PortalHeapMemory this,
			Pointer pointer,
			Size size)
{
    HeapMemoryBlock	block = HEAPMEMBLOCK(this);
    
    AssertState(PointerIsValid(block));
    
    return (AllocSetRealloc(&block->setData, pointer, size));
}

/* ----------------
 *	PortalHeapMemoryGetName
 * ----------------
 */
static char*
PortalHeapMemoryGetName(PortalHeapMemory this)
{
    return (form("%s-heap", PortalHeapMemoryGetPortal(this)->name));
}

/* ----------------
 *	PortalHeapMemoryDump
 * ----------------
 */
static void
PortalHeapMemoryDump(PortalHeapMemory this)
{
    HeapMemoryBlock	block;
    
    printf("--\n%s:\n", PortalHeapMemoryGetName(this));
    
    /* XXX is this the right interface */
    if (PointerIsValid(this->block))
	AllocSetDump(&HEAPMEMBLOCK(this)->setData);
    
    /* dump the stack too */
    for (block = (HeapMemoryBlock)FixedStackGetTop(&this->stackData);
	 PointerIsValid(block);
	 block = (HeapMemoryBlock)
	 FixedStackGetNext(&this->stackData, (Pointer)block)) {
	
	printf("--\n");
	AllocSetDump(&block->setData);
    }
}

/* ----------------------------------------------------------------
 *	 	variable / heap context method tables
 * ----------------------------------------------------------------
 */
static struct MemoryContextMethodsData	PortalVariableContextMethodsData = {
    PortalVariableMemoryAlloc,	/* Pointer (*)(this, uint32)	palloc */
    PortalVariableMemoryFree,	/* void (*)(this, Pointer)	pfree */
    PortalVariableMemoryRealloc,/* Pointer (*)(this, Pointer)	repalloc */
    PortalVariableMemoryGetName,/* char* (*)(this)		getName */
    PortalVariableMemoryDump	/* void (*)(this)		dump */
};

static struct MemoryContextMethodsData	PortalHeapContextMethodsData = {
    PortalHeapMemoryAlloc,	/* Pointer (*)(this, uint32)	palloc */
    PortalHeapMemoryFree,	/* void (*)(this, Pointer)	pfree */
    PortalHeapMemoryRealloc,    /* Pointer (*)(this, Pointer)	repalloc */
    PortalHeapMemoryGetName,    /* char* (*)(this)		getName */
    PortalHeapMemoryDump	/* void (*)(this)		dump */
};


/* ----------------------------------------------------------------
 *		  private internal support routines
 * ----------------------------------------------------------------
 */
/* ----------------
 *	CreateNewBlankPortal
 * ----------------
 */
static void
CreateNewBlankPortal()
{
    Portal	portal;
    
    AssertState(!PortalIsValid(BlankPortal));
    
    /*
     * make new portal structure
     */
    portal = (Portal)
	MemoryContextAlloc((MemoryContext)PortalMemory, sizeof *portal);
    
    /*
     * initialize portal variable context
     */
    NodeSetTag((Node*)&portal->variable, T_PortalVariableMemory);
    AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size)0);
    portal->variable.method = &PortalVariableContextMethodsData;
    
    /*
     * initialize portal heap context
     */
    NodeSetTag((Node*)&portal->heap, T_PortalHeapMemory);
    portal->heap.block = NULL;
    FixedStackInit(&portal->heap.stackData,
		   offsetof (HeapMemoryBlockData, itemData));
    portal->heap.method = &PortalHeapContextMethodsData;
    
    /*
     * set bogus portal name
     */
    portal->name = "** Blank Portal **";
    
    /* initialize portal query */
    portal->queryDesc = NULL;
    portal->attinfo = NULL;
    portal->state = NULL;
    portal->cleanup = NULL;
    
    /*
     * install blank portal
     */
    BlankPortal = portal;
}

bool
PortalNameIsSpecial(char *pname)
{
    if (strcmp(pname, VACPNAME) == 0)
	return true;
    return false;
}

/*
 * This routine is used to collect all portals created in this xaction
 * and then destroy them.  There is a little trickiness required as a
 * result of the dynamic hashing interface to getting every hash entry
 * sequentially.  Its use of static variables requires that we get every
 * entry *before* we destroy anything (destroying updates the hashtable
 * and screws up the sequential walk of the table). -mer 17 Aug 1992
 */
void
CollectNamedPortals(Portal *portalP, int destroy)
{
    static Portal *portalList = (Portal *)NULL;
    static int    listIndex = 0;
    static int    maxIndex = 9;
    
    if (portalList == (Portal *)NULL)
	portalList = (Portal *)malloc(10*sizeof(Portal));
    
    if (destroy != 0)
	{
	    int i;
	    
	    for (i = 0; i < listIndex; i++)
		PortalDestroy(&portalList[i]);
	    listIndex = 0;
	}
    else
	{
	    Assert(portalP);
	    Assert(*portalP);
	    
	    /*
	     * Don't delete special portals, up to portal creator to do this
	     */
	    if (PortalNameIsSpecial((*portalP)->name))
		return;
	    
	    portalList[listIndex] = *portalP;
	    listIndex++;
	    if (listIndex == maxIndex)
		{
		    portalList = (Portal *)
			realloc(portalList, (maxIndex+11)*sizeof(Portal));
		    maxIndex += 10;
		}
	}
    return;
}

void
AtEOXact_portals()
{
    HashTableWalk(PortalHashTable, CollectNamedPortals, 0);
    CollectNamedPortals(NULL, 1);
}

/* ----------------
 *	PortalDump
 * ----------------
 */
#ifdef NOT_USED
static void
PortalDump(Portal *thisP)
{
    /* XXX state/argument checking here */
    
    PortalVariableMemoryDump(PortalGetVariableMemory(*thisP));
    PortalHeapMemoryDump(PortalGetHeapMemory(*thisP));
}
#endif

/* ----------------
 *	DumpPortals
 * ----------------
 */
#ifdef NOT_USED
static void
DumpPortals()
{
    /* XXX state checking here */
    
    HashTableWalk(PortalHashTable, PortalDump, 0);
}
#endif
 
/* ----------------------------------------------------------------
 *		   public portal interface functions
 * ----------------------------------------------------------------
 */
/*
 * EnablePortalManager --
 *	Enables/disables the portal management module.
 */
void
EnablePortalManager(bool on)
{
    static bool	processing = false;
    HASHCTL ctl;
    
    AssertState(!processing);
    AssertArg(BoolIsValid(on));
    
    if (BypassEnable(&PortalManagerEnableCount, on)) 
	return;
    
    processing = true;
    
    if (on) {	/* initialize */
	EnableMemoryContext(true);
	
	PortalMemory = CreateGlobalMemory(PortalMemoryName);
	
	ctl.keysize = MAX_PORTALNAME_LEN;
	ctl.datasize = sizeof(Portal);

	/* use PORTALS_PER_USER, defined in utils/portal.h
	 * as a guess of how many hash table entries to create, initially
	 */
	PortalHashTable = hash_create(PORTALS_PER_USER * 3, &ctl, HASH_ELEM);
	
	CreateNewBlankPortal();
	
    } else {	/* cleanup */
	if (PortalIsValid(BlankPortal)) {
	    PortalDestroy(&BlankPortal);
	    MemoryContextFree((MemoryContext)PortalMemory,
			      (Pointer)BlankPortal);
	    BlankPortal = NULL;
	}
	/*
	 * Each portal must free its non-memory resources specially.
	 */
	HashTableWalk(PortalHashTable, PortalDestroy, 0);
	hash_destroy(PortalHashTable);
	PortalHashTable = NULL;
	
	GlobalMemoryDestroy(PortalMemory);
	PortalMemory = NULL;
	
	EnableMemoryContext(true);
    }
    
    processing = false;
}

/*
 * GetPortalByName --
 *	Returns a portal given a portal name; returns blank portal given
 * NULL; returns invalid portal if portal not found.
 *
 * Exceptions:
 *	BadState if called when disabled.
 */
Portal
GetPortalByName(char *name)
{
    Portal	portal;
    
    AssertState(PortalManagerEnabled);
    
    if (PointerIsValid(name)) {
	PortalHashTableLookup(name, portal);
    }
    else {
	if (!PortalIsValid(BlankPortal))
	    CreateNewBlankPortal();
	portal = BlankPortal;
    }
    
    return (portal);
}

/*
 * BlankPortalAssignName --
 *	Returns former blank portal as portal with given name.
 *
 * Side effect:
 *	All references to the former blank portal become incorrect.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadState if called without an intervening call to GetPortalByName(NULL).
 *	BadArg if portal name is invalid.
 *	"WARN" if portal name is in use.
 */
Portal
BlankPortalAssignName(char *name)	/* XXX PortalName */
{
    Portal	portal;
    uint16	length;
    
    AssertState(PortalManagerEnabled);
    AssertState(PortalIsValid(BlankPortal));
    AssertArg(PointerIsValid(name));	/* XXX PortalName */
    
    portal = GetPortalByName(name);
    if (PortalIsValid(portal)) {
	elog(NOTICE, "BlankPortalAssignName: portal %s already exists", name);
	return (portal);
    }
    
    /*
     * remove blank portal
     */
    portal = BlankPortal;
    BlankPortal = NULL;
    
    /*
     * initialize portal name
     */
    length = 1 + strlen(name);
    portal->name = (char*)
	MemoryContextAlloc((MemoryContext)&portal->variable, length);
    
    strncpy(portal->name, name, length);
    
    /*
     * put portal in table
     */
    PortalHashTableInsert(portal);
    
    return (portal);
}

/*
 * PortalSetQuery --
 *	Attaches a "query" to portal.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal is invalid.
 *	BadArg if queryDesc is "invalid."
 *	BadArg if state is "invalid."
 */
void
PortalSetQuery(Portal portal,
	       QueryDesc *queryDesc,
	       TupleDesc attinfo,
	       EState *state,
	       void (*cleanup)(Portal portal))
{
    AssertState(PortalManagerEnabled);
    AssertArg(PortalIsValid(portal));
    AssertArg(IsA((Node*)state,EState));
    
    portal->queryDesc = queryDesc;
    portal->state = state;
    portal->attinfo = attinfo;
    portal->cleanup = cleanup;
}

/*
 * PortalGetQueryDesc --
 *	Returns query attached to portal.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal is invalid.
 */
QueryDesc *
PortalGetQueryDesc(Portal portal)
{
    AssertState(PortalManagerEnabled);
    AssertArg(PortalIsValid(portal));
    
    return (portal->queryDesc);
}

/*
 * PortalGetState --
 *	Returns state attached to portal.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal is invalid.
 */
EState *
PortalGetState(Portal portal)
{
    AssertState(PortalManagerEnabled);
    AssertArg(PortalIsValid(portal));
    
    return (portal->state);
}

/*
 * CreatePortal --
 *	Returns a new portal given a name.
 *
 * Note:
 *	This is expected to be of very limited usability.  See instead,
 * BlankPortalAssignName.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal name is invalid.
 *	"WARN" if portal name is in use.
 */
Portal
CreatePortal(char *name)	/* XXX PortalName */
{
    Portal	portal;
    uint16	length;
    
    AssertState(PortalManagerEnabled);
    AssertArg(PointerIsValid(name));	/* XXX PortalName */
    
    portal = GetPortalByName(name);
    if (PortalIsValid(portal)) {
	elog(NOTICE, "CreatePortal: portal %s already exists", name);
	return (portal);
    }
    
    /* make new portal structure */
    portal = (Portal)
	MemoryContextAlloc((MemoryContext)PortalMemory, sizeof *portal);
    
    /* initialize portal variable context */
    NodeSetTag((Node*)&portal->variable, T_PortalVariableMemory);
    AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size)0);
    portal->variable.method = &PortalVariableContextMethodsData;
    
    /* initialize portal heap context */
    NodeSetTag((Node*)&portal->heap, T_PortalHeapMemory);
    portal->heap.block = NULL;
    FixedStackInit(&portal->heap.stackData,
		   offsetof (HeapMemoryBlockData, itemData));
    portal->heap.method = &PortalHeapContextMethodsData;
    
    /* initialize portal name */
    length = 1 + strlen(name);
    portal->name = (char*)
	MemoryContextAlloc((MemoryContext)&portal->variable, length);
    strncpy(portal->name, name, length);
    
    /* initialize portal query */
    portal->queryDesc = NULL;
    portal->attinfo = NULL;
    portal->state = NULL;
    portal->cleanup = NULL;
    
    /* put portal in table */
    PortalHashTableInsert(portal);
    
    /* Trap(PointerIsValid(name), Unimplemented); */
    return (portal);
}

/*
 * PortalDestroy --
 *	Destroys portal.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal is invalid.
 */
void
PortalDestroy(Portal *portalP)
{
    Portal portal = *portalP;
    
    AssertState(PortalManagerEnabled);
    AssertArg(PortalIsValid(portal));
    
    /* remove portal from table if not blank portal */
    if (portal != BlankPortal)
	PortalHashTableDelete(portal);
    
    /* reset portal */
    if (PointerIsValid(portal->cleanup))
	(*portal->cleanup)(portal);
    
    PortalResetHeapMemory(portal);
    MemoryContextFree((MemoryContext)&portal->variable,
		      (Pointer)portal->name);
    AllocSetReset(&portal->variable.setData);	/* XXX log */
    
    if (portal != BlankPortal)
	MemoryContextFree((MemoryContext)PortalMemory, (Pointer)portal);
}

/* ----------------
 *	PortalResetHeapMemory --
 *		Resets portal's heap memory context.
 *
 * Someday, Reset, Start, and End can be optimized by keeping a global
 * portal module stack of free HeapMemoryBlock's.  This will make Start
 * and End be fast.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadState if called when not in PortalHeapMemory context.
 *	BadArg if mode is invalid.
 * ----------------
 */
void
PortalResetHeapMemory(Portal portal)
{
    PortalHeapMemory	context;
    MemoryContext	currentContext;
    
    context = PortalGetHeapMemory(portal);
    
    if (PointerIsValid(context->block)) {
	/* save present context */
	currentContext = MemoryContextSwitchTo((MemoryContext)context);
	
	do {
	    EndPortalAllocMode();
	} while (PointerIsValid(context->block));
	
	/* restore context */
	(void) MemoryContextSwitchTo(currentContext);
    }
}

/*
 * StartPortalAllocMode --
 *	Starts a new block of portal heap allocation using mode and limit;
 *	the current block is disabled until EndPortalAllocMode is called.
 *
 * Note:
 *	Note blocks may be stacked and restored arbitarily.
 *	The semantics of mode and limit are described in aset.h.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadState if called when not in PortalHeapMemory context.
 *	BadArg if mode is invalid.
 */
void
StartPortalAllocMode(AllocMode mode, Size limit)
{
    PortalHeapMemory	context;
    
    AssertState(PortalManagerEnabled);
    AssertState(IsA(CurrentMemoryContext,PortalHeapMemory));
    /* AssertArg(AllocModeIsValid); */
    
    context = (PortalHeapMemory)CurrentMemoryContext;
    
    /* stack current mode */
    if (PointerIsValid(context->block))
	FixedStackPush(&context->stackData, context->block);
    
    /* allocate and initialize new block */
    context->block =
	MemoryContextAlloc(
			   (MemoryContext)PortalHeapMemoryGetVariableMemory(context),
			   sizeof (HeapMemoryBlockData) );
    
    /* XXX careful, context->block has never been stacked => bad state */
    
    AllocSetInit(&HEAPMEMBLOCK(context)->setData, mode, limit);
}

/*
 * EndPortalAllocMode --
 *	Ends current block of portal heap allocation; previous block is
 *	reenabled.
 *
 * Note:
 *	Note blocks may be stacked and restored arbitarily.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadState if called when not in PortalHeapMemory context.
 */
void
EndPortalAllocMode()
{
    PortalHeapMemory	context;
    
    AssertState(PortalManagerEnabled);
    AssertState(IsA(CurrentMemoryContext,PortalHeapMemory));
    
    context = (PortalHeapMemory)CurrentMemoryContext;
    AssertState(PointerIsValid(context->block));	/* XXX Trap(...) */
    
    /* free current mode */
    AllocSetReset(&HEAPMEMBLOCK(context)->setData);
    MemoryContextFree((MemoryContext)PortalHeapMemoryGetVariableMemory(context),
		      context->block);
    
    /* restore previous mode */
    context->block = FixedStackPop(&context->stackData);
}

/*
 * PortalGetVariableMemory --
 *	Returns variable memory context for a given portal.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal is invalid.
 */
PortalVariableMemory
PortalGetVariableMemory(Portal portal)
{
    return (&portal->variable);
}

/*
 * PortalGetHeapMemory --
 *	Returns heap memory context for a given portal.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if portal is invalid.
 */
PortalHeapMemory
PortalGetHeapMemory(Portal portal)
{
    return (&portal->heap);
}

/*
 * PortalVariableMemoryGetPortal --
 *	Returns portal containing given variable memory context.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if context is invalid.
 */
Portal
PortalVariableMemoryGetPortal(PortalVariableMemory context)
{
    return ((Portal)((char *)context - offsetof (PortalD, variable)));
}

/*
 * PortalHeapMemoryGetPortal --
 *	Returns portal containing given heap memory context.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if context is invalid.
 */
Portal
PortalHeapMemoryGetPortal(PortalHeapMemory context)
{
    return ((Portal)((char *)context - offsetof (PortalD, heap)));
}

/*
 * PortalVariableMemoryGetHeapMemory --
 *	Returns heap memory context associated with given variable memory.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if context is invalid.
 */
PortalHeapMemory
PortalVariableMemoryGetHeapMemory(PortalVariableMemory context)
{
    return ((PortalHeapMemory)((char *)context
			       - offsetof (PortalD, variable)
			       + offsetof (PortalD, heap)));
}

/*
 * PortalHeapMemoryGetVariableMemory --
 *	Returns variable memory context associated with given heap memory.
 *
 * Exceptions:
 *	BadState if called when disabled.
 *	BadArg if context is invalid.
 */
PortalVariableMemory
PortalHeapMemoryGetVariableMemory(PortalHeapMemory context)
{
    return ((PortalVariableMemory)((char *)context
				   - offsetof (PortalD, heap)
				   + offsetof (PortalD, variable)));
}

